# python-resmon - Python tools for Resmon monitoring and the Circonus API
# Copyright (c) 2010 Crown coypright
# 
# This file is part of python-resmon.
# 
# python-resmon is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# python-resmon is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with python-resmon.  If not, see <http://www.gnu.org/licenses/>.

import sys
import os.path
import re
import time
import random

import traceback
import Queue as queue
from threading import Thread

import socket
import urllib2
import httplib
import irclib

import settings


class IRCProxy(Thread):
    def __init__(self):
        super(IRCProxy, self).__init__()
        self.irc = irclib.IRC()
        self.irc.add_global_handler('all_events', self.print_ev)

        # register when we receive the welcome message
        if self.need_nickserv():
            self.irc.add_global_handler('welcome', self.register)
        else:
            self.irc.add_global_handler('welcome', self.join_channel)

        self.irc.add_global_handler('disconnect', self.on_disconnect)
        self.irc.add_global_handler('pubmsg', self.recv_pubmsg)

        self.server = self.irc.server()
        self.server.connect(settings.IRC_SERVER, 6667, settings.IRC_USERNAME)
        self.outqueue = queue.Queue()
        self.inqueue = queue.Queue()
        self.keeprunning = True

    def print_ev(self, connection, event):
        print event.eventtype(), event.source(), event.target(), event.arguments()

    def need_nickserv(self):
        return bool(getattr(settings, 'IRC_NICKSERV_PASSWORD', ''))

    def register(self, connection, event):
        if self.need_nickserv():
            self.server.privmsg('NickServ', 'identify %s' % settings.IRC_NICKSERV_PASSWORD)
            # join the channel when we are registered
            self.irc.add_global_handler('umode', self.join_channel_on_register)

    def join_channel_on_register(self, connection, event):
        if event.source() == self.username() and event.arguments() == ['+r']:
            self.server.join(settings.IRC_CHANNEL)

    def ensure_visible(self, connection, event):
        if event.source() == self.username() and event.arguments() == ['+i']:
            self.server.mode(self.username(), '-i')

    def join_channel(self, connection, event):
        self.server.join(settings.IRC_CHANNEL)

    def username(self):
        return settings.IRC_USERNAME

    def channel(self):
        return settings.IRC_CHANNEL.split()[0]

    def recv_pubmsg(self, connection, event):
        msg = event.arguments()[0]
        if event.target == self.username():
            self.inqueue.put(msg)
        elif msg.startswith(self.username()):
            self.inqueue.put(re.sub(r'^([:,]?\s+)', '', msg[len(self.username()):]))

    def run(self):
        while self.keeprunning:
            try:
                msg = self.outqueue.get_nowait()
            except queue.Empty:
                pass
            else:
                self.server.privmsg(self.channel(), msg.encode(settings.IRC_CHARSET))
            self.irc.process_once(0.5)
        self.disconnect()
        
    def stop(self):
        self.keeprunning = False
        self.join()

    def disconnect(self):
        if self.server:
            s = self.server
            self.server = None
            s.disconnect(random.choice(['Bye', 'Auf wiedersehen', 'Ciao', 'Laters', 'TTFN']))

    def on_disconnect(self, connection, event):
        """Called on disconnect that we didn't intend."""
        if self.server:
            self.server.connect(settings.IRC_SERVER, 6667, settings.IRC_USERNAME)
        
    def say(self, msg):
        self.outqueue.put(msg)

    def __del__(self):
        if self.is_alive():
            self.stop()


class BotPlugin(object):
    """Abstract interface for bot plugins."""

    POLL_TIME = 60

    def poll(self):
        """Called periodically in order to poll web services etc.
        
        Yields a list of messages to issue to the channel."""


    def handle_msg(self, msg):
        """Subclasses should implement this method to handle channel messages"""


class Bot(object):
    def __init__(self, plugins=[]):
        socket.setdefaulttimeout(30)
        self.irc = None
        self.keeprunning = True
        self.plugins = plugins

    def start_irc(self):
        self.irc = IRCProxy()
        self.irc.start()

    def stop_irc(self):
        self.irc.stop()

    def start(self):
        try:
            self.run()
        finally:
            self.stop_irc()

    def run(self):
        t = 0
        while self.keeprunning:
            for p in self.plugins:
                if t % p.POLL_TIME == 0:
                    try:
                        resp = p.poll()
                        if resp:
                            for m in resp:
                                self.irc.say(m)
                    except:
                        traceback.print_exc()
            while True:
                try:
                    msg = self.irc.inqueue.get_nowait()
                except queue.Empty:
                    break
                
                for p in self.plugins:
                    try:
                        resp = p.handle_message(msg)
                        if resp:
                            for m in resp:
                                self.irc.say(m)
                    except:
                        traceback.print_exc()
            time.sleep(1)
            t += 1
        self.stop_irc()
